#include "MapCacher.h"
#include "drogon/drogon.h"
#include "OCMain/Environment.h"
#include "OCMain/FileUtil.h"
#include "OCMain/Log.h"
#include "MapCacherWorkQueue.h"

namespace OCPP
{
	std::unique_ptr<MapCacherWorkQueue> MapCacher::_mapCacherWorkQueue;

	void MapCacher::shutdown()
	{
		_mapCacherWorkQueue = nullptr;
	}

	MapCacher::MapCacher(String url, String cachePath, String ext, StringVector subdomains)
		:_url(url)
		, _path(cachePath)
		, _ext(ext)
		, _subdomains(subdomains)
	{

	}

	MapCacher::~MapCacher()
	{

	}

	void MapCacher::init()
	{
		if (_mapCacherWorkQueue == nullptr) {
			_mapCacherWorkQueue = std::make_unique<MapCacherWorkQueue>();
		}
	}

	String MapCacher::GenUrl(String layer, int z, int y, int x)
	{
		String url = StringUtil::replaceAll(_url, "{z}", StringConverter::toString(z));
		url = StringUtil::replaceAll(url, "{y}", StringConverter::toString(y));
		url = StringUtil::replaceAll(url, "{x}", StringConverter::toString(x));

		size_t domainSize = _subdomains.size();
		if (domainSize > 0)
		{
			size_t r = Math::UnitRandom() * domainSize;
			if (r > domainSize) {
				r = 0;
			}
			url = StringUtil::replaceAll(url, "{subdomains}", _subdomains[r]);
		}

		return url;
	}

	bool MapCacher::exist(String layer, int z, int y, int x)
	{
		String name = Stringify() << x << "." << _ext;
		String path = Stringify() << layer << "/" << z
			<< "/" << y;

		String cacheLayerPath = Stringify() << path << "/" << name;

		String fileName = joinPath(_path, cacheLayerPath);

		return FileUtil::existFile(fileName);
	}

	String MapCacher::getCacheSync(String layer, String path)
	{
		String simpleFileName = Stringify() << path.substr(0,path.find("?"));
		String name, ext, path1;
		StringUtil::splitFullFilename(simpleFileName, name, ext, path1);

		//String s1 = StringUtil::decodeBase64(name);
		simpleFileName = joinPath(path1, drogon::utils::getSha1(name) + "." + ext);

		String fileName = joinPath(_path, simpleFileName);

		String data = FileUtil::getFileBinary(fileName);
		if (!data.empty() || _forbiddens[layer])
		{
			OCPP_LOG_INFO << "get in caches\n";
			return data;
		}

		String url = joinPath(_url, path);
		long lHTTPSCode;
		HttpClientEx httpClient([&](const String& message)
			{
				_forbiddens[layer] = true;
				OCPP_LOG_ERROR << message << "\n";
			});
		httpClient.SetProxy("127.0.0.1:7897");
		HttpClient::UserAgent = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36";
		HttpClient::Referer = "www.cesium.com";
		httpClient.SetTimeout(10);

		long retCode = 0;
		data = httpClient.fetchContent(url, retCode);
		if (retCode != 200)
		{
			OCPP_LOG_ERROR << U2A(data) << "\n";
			MapCacherWorkQueue::_badLayerMap[layer]++;
		}
		else
		{
			String path = FileUtil::getFilePath(fileName);
			if (!FileUtil::existFile(path))
			{
				FileUtil::makeDirectory(path);
			}
			FileUtil::writeFile(fileName, data);
			OCPP_LOG_INFO << fileName << " new Cache.\n";
			MapCacherWorkQueue::_badLayerMap[layer] = 0;
		}
		return data;
	}

	String MapCacher::getCacheSync(String layer, int z, int y, int x)
	{
		String name = Stringify() << x << "." << _ext;
		String path = Stringify() << layer << "/" << z
			<< "/" << y;

		String cacheLayerPath = Stringify() << path << "/" << name;

		String fileName = joinPath(_path, cacheLayerPath);

		String data = FileUtil::getFileBinary(fileName);
		if (!data.empty())
		{
			return data;
		}

		String url = GenUrl(layer, z, y, x);
		long lHTTPSCode;
		HttpClientEx httpClient([](const String& message)
			{
				OCPP_LOG_ERROR << message << "\n";
			});
		HttpClient::UserAgent = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36";
		HttpClient::Referer = "www.tianditu.gov.cn";
		httpClient.SetTimeout(10);

		long retCode = 0;
		data = httpClient.fetchContent(url, retCode);
		if (retCode != 200)
		{
			OCPP_LOG_ERROR << U2A(data) << "\n";
			MapCacherWorkQueue::_badLayerMap[layer]++;
		}
		else
		{
			path = joinPath(_path, path);
			if (!FileUtil::existFile(path))
			{
				FileUtil::makeDirectory(path);
			}
			FileUtil::writeFile(fileName, data);
			OCPP_LOG_INFO << fileName << " new Cache.\n";
			MapCacherWorkQueue::_badLayerMap[layer] = 0;
		}
		return data;
	}


	String MapCacher::getCache(String layer, String path)
	{
		if (!_initilized) {
			init();
			_initilized = true;
		}

		String cacheLayerPath = Stringify() << path;

		String fileName = joinPath(_path, cacheLayerPath);

		String data = FileUtil::getFileBinary(fileName);
		if (!data.empty())
		{
			return data;
		}

		if (MapCacherWorkQueue::_badLayerMap[layer] > 10)
		{
			return "";
		}

		CORE_LOCK_AUTO_MUTEX;
		if (_missingMap.find(cacheLayerPath) == _missingMap.end())
		{
			_missingMap[cacheLayerPath] = "";
			String url = "https://tile.googleapis.com/v1";
			_mapCacherWorkQueue->addDowload(url, joinPath(_path, path), layer, fileName);
		}

		return "";
	}

	String MapCacher::getCache(String layer, int z, int y, int x)
	{
		if (!_initilized) {
			init();
			_initilized = true;
		}
		String name = Stringify() << x << "." << _ext;
		String path = Stringify() << layer << "/" << z
			<< "/" << y;

		String cacheLayerPath = Stringify() << path << "/" << name;

		String fileName = joinPath(_path, cacheLayerPath);

		String data = FileUtil::getFileBinary(fileName);
		if (!data.empty())
		{
			return data;
		}

		if (MapCacherWorkQueue::_badLayerMap[layer] > 10)
		{
			return "";
		}

		CORE_LOCK_AUTO_MUTEX;
		if (_missingMap.find(cacheLayerPath) == _missingMap.end())
		{
			_missingMap[cacheLayerPath] = "";
			String url = GenUrl(layer, z, y, x);
			_mapCacherWorkQueue->addDowload(url, joinPath(_path, path), layer, fileName);
		}

		return "";
	}
}
